package cn.mini.mybatis.mybatis.datasource.unpool;

import cn.mini.mybatis.mybatis.datasource.DataSourceWrapper;
import cn.mini.mybatis.mybatis.io.Resources;

import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;


@SuppressWarnings("all")
public class UnpooledDataSource extends DataSourceWrapper {

    private ClassLoader driverClassLoader;
    //作为可选项,你可以传递数据库驱动的属性。要这样做,属性的前缀是以“driver.”开 头的,例如
    //driver.encoding=UTF8
    private Properties driverProperties;
    private static Map<String, Driver> registeredDrivers = new ConcurrentHashMap<String, Driver>();
    private String driver;
    private String url;
    private String username;
    private String password;

    private Boolean autoCommit;
    private Integer defaultTransactionIsolationLevel;

    static {
        Enumeration<Driver> drivers = DriverManager.getDrivers();
        while (drivers.hasMoreElements()) {
            Driver driver = drivers.nextElement();
            registeredDrivers.put(driver.getClass().getName(), driver);
        }
    }

    public UnpooledDataSource() {
    }

    public UnpooledDataSource(String driver, String url, String username, String password) {
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public UnpooledDataSource(String driver, String url, Properties driverProperties) {
        this.driver = driver;
        this.url = url;
        this.driverProperties = driverProperties;
    }

    public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, String username, String password) {
        this.driverClassLoader = driverClassLoader;
        this.driver = driver;
        this.url = url;
        this.username = username;
        this.password = password;
    }

    public UnpooledDataSource(ClassLoader driverClassLoader, String driver, String url, Properties driverProperties) {
        this.driverClassLoader = driverClassLoader;
        this.driver = driver;
        this.url = url;
        this.driverProperties = driverProperties;
    }

    /***
     * 继承自DataSource所必须重写方法
     */

    @Override
    public Connection getConnection() throws SQLException {
        return doGetConnection(username, password);
    }


    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return doGetConnection(username, password);
    }

    private Connection doGetConnection(String username, String password) throws SQLException {
        Properties connectionProps = buildDataSourceConnectionPropertis(username,password);
        if (connectionProps == null) {
            throw new SQLException("properties error , please check datasource username or password");
        }
        // 初始化驱动
        initializeDriver();
        Connection connection = DriverManager.getConnection(url,connectionProps);
        configureConnection(connection);
        return connection;
    }

    private Properties buildDataSourceConnectionPropertis(String username, String password) {
        Properties properties = new Properties();
        if (driverProperties != null) {
            properties.putAll(driverProperties);
        }
        if (username == null || password == null) {
            return  null;
        }
        properties.put("user",username);
        properties.put("password",password);
        return properties;
    }


    private synchronized void initializeDriver() throws SQLException {
        if (!registeredDrivers.containsKey(driver)) {
            Class<?> driverType ;
            try {
                if (driverClassLoader != null) {
                    driverType = Class.forName(driver,true,driverClassLoader);
                } else {
                    driverType = Resources.classForName(driver);
                }
                Driver driverInstanse = (Driver) driverType.newInstance();
                DriverManager.registerDriver(driverInstanse);
                registeredDrivers.put(driver,driverInstanse);
            }catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex ) {
                throw new SQLException("Error setting driver on UnpooledDataSource. Cause: " + ex);
            }
        }
    }

    private void configureConnection(Connection conn) throws SQLException {
        if (autoCommit != null && autoCommit != conn.getAutoCommit()) {
            conn.setAutoCommit(autoCommit);
        }
        if (defaultTransactionIsolationLevel != null) {
            conn.setTransactionIsolation(defaultTransactionIsolationLevel);
        }
    }



    /***
     * UnpooledDataSource内部属性相关属性get/set方法
     * @return
     */


    public ClassLoader getDriverClassLoader() {
        return driverClassLoader;
    }

    public void setDriverClassLoader(ClassLoader driverClassLoader) {
        this.driverClassLoader = driverClassLoader;
    }

    public Properties getDriverProperties() {
        return driverProperties;
    }

    public void setDriverProperties(Properties driverProperties) {
        this.driverProperties = driverProperties;
    }

    public String getDriver() {
        return driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
    public Boolean isAutoCommit() {
        return autoCommit;
    }

    public void setAutoCommit(Boolean autoCommit) {
        this.autoCommit = autoCommit;
    }

    public Integer getDefaultTransactionIsolationLevel() {
        return defaultTransactionIsolationLevel;
    }
}
